/**
 * A very early prototype of the table plugin for jQuery.
 * 
 * @addon
 * @author Tin
 * @version 0.1.11
 */
(function($) {
    "use strict";
    var jQuery, namespace, defaultSettings, functions, methods;
    if (typeof String.prototype.startsWith !== "function") {
        String.prototype.startsWith = function(str) {
            return this.lastIndexOf(str, 0) === 0;
        }
    }
    if (typeof String.prototype.capitalize !== "function") {
        String.prototype.capitalize = function() {
            return this.charAt(0).toUpperCase() + this.slice(1);
        }
    }
    /**
     * @constant jQuery
     */
    jQuery = $; /// for scope safety.
    /**
     * @constant namespace
     */
    namespace = "table",
    /**
     * 
     */
    defaultSettings = {

    };

    /**
     * Internal utility functions.
     * <p>
     * These functions do not generally map the [this] keyword back to the
     * plugin's jQuery object. They are intended to be called statically (i.e.
     * not invoked using the {@code apply} or {@code call} methods), and any
     * required references, including the jQuery object itself, must be passed
     * in as arguments. In other words, the [this] keyword within the scope of
     * these functions does <strong>not</strong> reference the jQuery object as
     * it does with methods.
     * <p>
     * Functions also differ to methods in that they are not exposed to the
     * plugin's public API, i.e. they cannot be invoked outside the plugin's
     * scope.
     * 
     * @namespace
     */
    functions = {
        /**
         * @function
         * @param {object} settings
         * @param {string} option
         * @param {string} value
         */
        _setOption : function(settings, option, value) {
            console.log(settings, option, value);
            if (settings[option] && option.startsWith('_')) {
                $.error("Illegal Access: Cannot modify the field " + option);
            } else {
                settings[option] = value;
            }
        },

        /**
         * @function
         * @param {object} data
         * @param {object} [options]
         */
        _setOptions : function(data, options) {
            var settings = data._settings || $.extend({}, defaultSettings);
            if (typeof options === "object") {
                $.each(options, function(option, value) {
                    functions._setOption(settings, option, value);
                });
            }
            data._settings = settings;
        }
    };

    /**
     * Public methods exposed to the plugin's API, as well as a number of
     * private methods that are accessible only within the plugin's scope.
     * <p>
     * Methods are declared private, by convention, with an underscore ('_')
     * prefix. Such methods are protected from access; attempting to call a
     * method that is declared private will result in a jQuery runtime error.
     * 
     * @namespace
     */
    methods = {
        /**
         * @param {object} queryOptions
         * @returns this jQuery object
         */
        select : function() {
            console.log("Called tableSelect on", this);
            var that;
            that = this;

            /// TODO run select query here >>

            return that;
        },

        /**
         * @param {object} [groupSettings]
         * @param {object[]} records
         * @returns this jQuery object
         */
        insert : function() {
            console.log("Called tableInsert on", this);
            var that, data;
            that = this;
            data = that.data(namespace);

            /// TODO run insert operation here >>
            $.each(arguments, function(i, record) {
                if (typeof record === "object") {
                    var row;

                    /// Construct a new row from the provided record...
                    (function() {
                        var cellData;
                        cellData = ($.isArray(record.data)) ? record.data : [];
                        console.log(cellData, $.isArray(record.data));
                        row = $("<tr />", {
                            class : cellData["class"],
                            id : cellData["id"]
                        });

                        /// Insert cell data into the new row...
                        (function(row, cellData) {
                            var cell;
                            for ( var i = 0; i < data.fields.length; i++) {
                                if (cellData[i]) cell = cellData[i];
                                else if (cellData[i] === null) cell = "";
                                else if (typeof cellData[i] !== "undefined") {
                                    cell = new Object(cellData[i]).toString();
                                } else cell = "";
                                row.append($("<td />", {
                                    html : cell
                                }));
                            }
                        }).call(that, row, cellData);
                    }).apply(that);

                    /// Insert the newly created row into table...
                    (function(row) {
                        var body = this.find("tbody");
                        if (!body.length) body = $("<tbody />").appendTo(this);
                        body.append(row);
                    }).call(that, row);

                    /// Set any necessary data...
                    (function() {

                    });

                    /// Deal with the after() callback...
                    (function(record, row) {
                        if (typeof record.after === "function") {
                            record.after.apply(row);
                        }
                    }).call(that, record, row);
                } else {
                    $.error("Illegal Argument: at tableInsert.");
                }
            });
            return that;
        },

        /**
         * @param {object} query
         * @returns this jQuery object
         */
        _delete : function() {
            console.log("Called tableDelete on", this);
            var that;
            that = this;

            /// TODO run delete operation here >>

            return that;
        },

        /**
         * @param {object} query
         * @param {object} data
         * @returns this jQuery object
         */
        _update : function() {
            console.log("Called tableUpdate on", this);
            var that;
            that = this;

            /// TODO run update operation here >>

            return that;
        },

        /**
         * @param {object[]} fields
         * @returns this jQuery object
         */
        add : function() {
            console.log("Called tableAdd on", this);
            var that, data, colData, colHook;
            that = this;
            data = that.data(namespace);

            /// TODO run add operation here >>

            $.each(arguments, function(i, arg) {
                if (typeof arg === "object") {
                    colData = arg;
                    (function() {
                        data.fields.push(colData);

                        /// Insert a new header for this column...
                        (function() {
                            var headers = that.find("thead");
                            if (!headers.length) headers = $("<thead />", {
                                html : undefined
                            }).appendTo(that).append("<tr></tr>");
                            headers.find("tr:first").append($("<th />", {
                                class : colData.fieldName + "-header",
                                html : colData.displayName
                            }));
                        }).apply(that);

                        /// Insert a <col /> element for this column...
                        (function() {
                            var colName = colData.fieldName;
                            colHook = that.find("col:last");
                            if (colHook.length) colHook.after($("<col />", {
                                class : namespace + "-" + colName + "-col",
                                id : colName + "-col"
                            }));
                            else {
                                colHook = that.find("colgroup");
                                if (!colHook.length) colHook = that;
                                colHook.prepend($("<col/>", {
                                    class : namespace + "-" + colName + "-col",
                                    id : colName + "-col"
                                }));
                            }
                        }).apply(that);

                        /// Calibrate the number of cells in all existing rows...
                        (function() {

                        });
                    }).apply(that);
                } else {
                    $.error("Illegal Argument: at tableAdd.");
                }
            });
            return that;
        },

        /**
         * @constructor
         * @param {object} [options]
         * @returns this jQuery object
         */
        init : function(options) {
            var data, settings;
            this.data(namespace, {
                _settings : undefined
            });
            data = this.data(namespace);
            functions._setOptions(data, options);

            /// TODO continue initialization here >>

            /// Find the max number of columns...
            (function() {
                var maxNumOfCols = 0;
                $.each(this.find("tr"), function(i, row) {
                    var thisRowNumOfCols = $(row).children().length;
                    if (thisRowNumOfCols > maxNumOfCols) {
                        maxNumOfCols = thisRowNumOfCols;
                    }
                });

                /// Calibrate with the number of <col /> elements...
                (function(data) {
                    data.fields = [];
                    data.fields.length = maxNumOfCols;
                }).call(this, data);
            }).apply(this);

            /// Enhance this table with additional convenience methods...
            (function() {
                var that = this;
                $.each(methods, function(method, fn) {
                    var apiMethod;
                    if (!method.startsWith('_')) {
                        apiMethod = namespace + method.capitalize();
                        that[apiMethod] = methods[method];
                    }
                });
            }).apply(this);
            return this;
        },

        /**
         * @returns this jQuery object
         */
        _destroy : function() {
            return this;
        }
    };

    /**
     * Extends jQuery with a method that allows for certain jQuery objects to be
     * enhanced with additional convenience functions, behavior and properties
     * specific to tables, for convenient manipulation and management of tabular
     * data.
     * 
     * @namespace
     * @param {string} method
     * @param {object} [options]
     */
    jQuery.fn.asTable = function(method, options) {
        var args;
        if (typeof method === "string" && methods[method]) {
            if (method.startsWith('_')) {
                $.error("Error: The method '" + method + "' is not visible.");
            }
            console.info(namespace + "." + method + " called");
            args = Array.prototype.slice.call(arguments, 1);
            return methods[method].apply(this, args);
        } else if (typeof method === "object" || !method) {
            (function() {
                var which = (method ? 'with' : 'without');
                console.info(namespace + ".init called " + which + " options");
                if (method) console.log("options:", method);
            }).call();
            args = Array.prototype.slice.call(arguments);
            return methods.init.apply(this, args);
        } else {
            $.error("Error: The method '" + method + "' does not exist.");
        }
    };
}(jQuery));
